#pragma once

#include "../common/Common.h"
#include "../common/Message.h"

namespace rpc
{

    // 结果类型
    enum class VType
    {
        BOOL = 0,
        INTEGRAL,
        NUMERIC,
        STRING,
        ARRAY,
        OBJECT,
    };

    // 服务描述信息
    class ServiceDescribe
    {
    public:
        using ptr = std::shared_ptr<ServiceDescribe>;
        using Callback = std::function<void(const Json::Value &params, Json::Value &ret)>;
        using ParamDescribe = std::pair<std::string, VType>;

        ServiceDescribe(std::string &&mname, VType type, std::vector<ParamDescribe> paramDesc, Callback &&cb)
            : _method(std::move(mname)), _resultType(type), _paramDesc(std::move(paramDesc)), _cb(std::move(cb)) {}

        const std::string &method() const
        {
            return _method;
        }

        // 针对参数的校验
        bool paramCheck(const Json::Value &json) const
        {
            for (auto &desc : _paramDesc)
            {
                if (json.isMember(desc.first) == false)
                {
                    ELOG("调用 %s 方法中没有当前注册的方法指定的参数！", _method.c_str())
                    return false;
                }

                if (!check(desc.second, json[desc.first]))
                {
                    ELOG("调用 %s 方法的参数类型和注册的方法不同!", _method.c_str());
                    return false;
                }
            }
            return true;
        }

        // 调用方法
        bool call(const Json::Value &json, Json::Value &result)
        {
            _cb(json, result);
            if (!rTypeCheck(result))
            {
                ELOG("%s 方法调用返回值信息错误!", _method.c_str());
                return false;
            }

            return true;
        }

    private:
        bool rTypeCheck(const Json::Value &result)
        {
            return check(_resultType, result);
        }

        bool check(const VType type, const Json::Value &value) const
        {
            switch (type)
            {
            case VType::BOOL:
                return value.isBool();
            case VType::INTEGRAL:
                return value.isIntegral();
            case VType::NUMERIC:
                return value.isNumeric();
            case VType::STRING:
                return value.isString();
            case VType::ARRAY:
                return value.isArray();
            case VType::OBJECT:
                return value.isObject();
            }

            return false;
        }

    private:
        Callback _cb;                          // 回调函数
        VType _resultType;                     // 返回值类型
        std::string _method;                   // 方法名
        std::vector<ParamDescribe> _paramDesc; // 参数描述 {参数名：参数类型}
    };

    // 构建服务对象
    class ServiceDescribeFactory
    {
    public:
        void setMethod(string &&name)
        {
            _method = std::move(name);
        }

        //  服务方法
        void setCallback(ServiceDescribe::Callback &&cb)
        {
            _cb = std::move(cb);
        }

        void addParamDesc(const string &paramName, VType type)
        {
            _paramDesc.emplace_back(std::make_pair(paramName, type));
        }

        void setResultType(VType type)
        {
            _resultType = type;
        }

        ServiceDescribe::ptr build()
        {
            return std::make_shared<ServiceDescribe>(std::move(_method), _resultType, std::move(_paramDesc), std::move(_cb));
        }

    private:
        ServiceDescribe::Callback _cb;
        std::vector<ServiceDescribe::ParamDescribe> _paramDesc;
        string _method;
        VType _resultType;
    };

    // 服务管理
    class ServiceManager
    {
    public:
        using ptr = std::shared_ptr<ServiceManager>;

        void insert(const ServiceDescribe::ptr desc)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            _methods.insert({desc->method(), desc});
        }

        void remove(const ServiceDescribe::ptr desc)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            _methods.erase(desc->method());
        }

        ServiceDescribe::ptr select(const string &method)
        {
            unique_lock<mutex> lock(_mtx);

            auto it = _methods.find(method);
            if (it == _methods.end())
            {
                ELOG("没有要找的 %s 服务！", method.c_str())
                return ServiceDescribe::ptr();
            }

            return it->second;
        }

    private:
        std::mutex _mtx;
        unordered_map<std::string, ServiceDescribe::ptr> _methods;
    };

    class RpcRouter
    {
    public:
        using ptr = std::shared_ptr<RpcRouter>;

        RpcRouter() : _serviceManager(std::make_shared<ServiceManager>()) {}

        // 这个方法是dispatcher进行调用的
        void onRpcRequest(const BaseConnection::ptr &conn, RpcRequest::ptr &req)
        {
            // 首先查看当前方法服务中有没有要调用的方法
            auto desc = _serviceManager->select(req->method());
            if (!desc)
            {
                ELOG("%s 方法没有找到!", req->method().c_str());
                return response(conn, req, RCode::RCODE_NOT_FOUND_SERVICE, Json::Value());
            }

            // 验证参数
            if (!desc->paramCheck(req->params()))
            {
                ELOG("%s 方法调用时参数错误！", req->method().c_str());
                return response(conn, req, RCode::RCODE_INVALID_PARAMS, Json::Value());
            }

            Json::Value res;
            // 调用业务回调接口进行处理
            if (!desc->call(req->params(), res))
            {
                ELOG("%s 方法调用出错！", req->method().c_str());
                return response(conn, req, RCode::RCODE_INTERNAL_ERROR, Json::Value());
            }

            // 处理完毕进行响应
            response(conn, req, RCode::RCODE_OK, res);
        }

        void registerMethod(const ServiceDescribe::ptr &desc)
        {
            _serviceManager->insert(desc);
        }

    private:
        void response(const BaseConnection::ptr &conn, RpcRequest::ptr &req, RCode rcode, const Json::Value &result)
        {
            RpcResponse::ptr res = MessageFactory::create<RpcResponse>();
            res->setRid(req->rid());
            res->setMtype(MType::RSP_RPC);
            res->setRcode(rcode);
            res->setResult(result);

            conn->send(res);
        }

    private:
        ServiceManager::ptr _serviceManager;
    };

} // rpc